﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;

namespace Cassinipp
{
	public partial class MainForm : Form
	{
		private Server Server { get; set; }

		#region Constructors

		public MainForm()
		{
			InitializeComponent();
		}

		public MainForm(string[] args)
		{
			InitializeComponent();

			try
			{
				if (args.Length >= 1)
				{
					txtAppPath.Text = args[0];
				}

				if (args.Length >= 3)
				{
					txtVirtRoot.Text = args[2];
				}

				if (args.Length >= 4)
				{
					if (args[4].Equals("loopback", StringComparison.OrdinalIgnoreCase))
					{
						rdbLoopBack.Checked = true;
					}
					else
					{
						rdbAny.Checked = true;
					}
				}

				if (args.Length >= 2)
				{
					nudPort.Value = int.Parse(args[1]);
				}
			}
			catch
			{
			}

			if (ValidateForm())
			{
				Start();
			}
		}

		#endregion

		#region Start, Stop

		private void btnStart_Click(object sender, EventArgs e)
		{
			if (ValidateForm())
			{
				Start();
			}
		}

		private void btnStop_Click(object sender, EventArgs e)
		{
			Stop();
		}

		private void Start()
		{
			try
			{
				Server = new Server((int) nudPort.Value, txtVirtRoot.Text, txtAppPath.Text, rdbAny.Checked ? IPAddress.Any : IPAddress.Loopback);

				// Register event handlers
				Server.Started += new EventHandler(ServerStarted);
				Server.Stopped += new EventHandler(ServerStopped);

				Server.Start();
			}
			catch
			{
				ShowError("Cassini Managed Web Server failed to start listening on port " + nudPort.Value + ".\r\n" +
				          "Possible conflict with another Web Server on the same port.");
			}
		}

		private void Stop()
		{
			try
			{
				if (Server != null)
				{
					Server.Stop();
				}
			}
			finally
			{
				// Unregister event handlers
				if (Server != null)
				{
					Server.Started -= new EventHandler(ServerStarted);
					Server.Stopped -= new EventHandler(ServerStopped);
				}

				Server = null;
			}
		}

		private void ServerStopped(object sender, EventArgs e)
		{
			SetStatus("Cassini++ stopped");

			llaOpenBrowser.Text = String.Empty;

			EnableInput(true);
		}

		private void ServerStarted(object sender, EventArgs e)
		{
			SetStatus("Cassini++ running on port " + nudPort.Value);

			llaOpenBrowser.Text = Server.RootUrl;

			EnableInput(false);
		}

		#endregion

		#region Helpers

		private void ShowError(String err)
		{
			MessageBox.Show(err, "Cassini++", MessageBoxButtons.OK, MessageBoxIcon.Error);
		}

		private void SetStatus(string status)
		{
			tssStatus.Text = status;

			// Only 63 chars allowed in notify icon title
			noiCassini.Text = status.Substring(0, Math.Min(status.Length, 63));
		}

		private void EnableInput(bool stopped)
		{
			btnStart.Enabled = stopped;
			startToolStripMenuItem.Enabled = stopped;

			btnStop.Enabled = !stopped;
			stopToolStripMenuItem.Enabled = !stopped;

			txtVirtRoot.Enabled = stopped;
			nudPort.Enabled = stopped;
			txtAppPath.Enabled = stopped;
			rdbLoopBack.Enabled = stopped;
			rdbAny.Enabled = stopped;

			btnSelectDirectory.Enabled = stopped;
		}

		private void llaOpenBrowser_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			System.Diagnostics.Process.Start(llaOpenBrowser.Text);
		}

		#endregion

		#region Directory selection

		private void btnSelectDirectory_Click(object sender, EventArgs e)
		{
			var res = fbDlgSelectAppPath.ShowDialog(this);

			if (res == DialogResult.OK || res == DialogResult.Yes)
			{
				txtAppPath.Text = fbDlgSelectAppPath.SelectedPath;
			}
		}

		#endregion

		[STAThread]
		private static void Main(string[] args)
		{
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.Run(new MainForm(args));
		}

		#region Notify icon event handlers

		private void MainForm_Resize(object sender, EventArgs e)
		{
			if (WindowState == FormWindowState.Minimized)
			{
				ShowInTaskbar = false;
				noiCassini.Visible = true;
			}
			else if (WindowState == FormWindowState.Normal || WindowState == FormWindowState.Maximized)
			{
				ShowInTaskbar = true;
				noiCassini.Visible = false;
			}
		}

		private void noiCassini_DoubleClick(object sender, EventArgs e)
		{
			WindowState = FormWindowState.Normal;
		}

		private void startToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Start();
		}

		private void stopToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Stop();
		}

		private void exitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Close();
		}

		#endregion

		#region Validation

		private void nudPort_ValueChanged(object sender, EventArgs e)
		{
			ValidatePort();
		}

		private void txtAppPath_TextChanged(object sender, EventArgs e)
		{
			ValidateAppPath();
		}

		private void txtVirtRoot_TextChanged(object sender, EventArgs e)
		{
			ValidateVirtRoot();
		}

		private bool ValidateVirtRoot()
		{
			return Validate(() => !String.IsNullOrEmpty(txtVirtRoot.Text), lblVirtRoot);
		}

		private bool ValidateAppPath()
		{
			return Validate(() => !String.IsNullOrEmpty(txtAppPath.Text) && Directory.Exists(txtAppPath.Text), lblAppPath);
		}

		private bool ValidatePort()
		{
			return Validate(() => nudPort.Value > 0, lblPort);
		}

		private bool Validate(Func<bool> predicate, Label lbl)
		{
			var valid = predicate();

			if (!valid)
			{
				lbl.ForeColor = Color.Red;
			}
			else
			{
				lbl.ForeColor = Color.Black;
			}

			return valid;
		}

		private bool ValidateForm()
		{
			return ValidatePort() && ValidateAppPath() && ValidateVirtRoot();
		}

		#endregion
	}
}